package l1j.server.server.model;

import static l1j.server.server.model.skill.L1SkillId.BLIND_HIDING;
import static l1j.server.server.model.skill.L1SkillId.GMSTATUS_FINDINVIS;
import static l1j.server.server.model.skill.L1SkillId.ILLUSION_AVATAR;
import static l1j.server.server.model.skill.L1SkillId.INVISIBILITY;
import static l1j.server.server.model.skill.L1SkillId.LIGHT;
import static l1j.server.server.model.skill.L1SkillId.ILLUSION_LICH;

import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.Server.L1World.L1World;
import l1j.server.server.model.Instance.L1FollowerInstance;
import l1j.server.server.model.Instance.L1ItemInstance;
import l1j.server.server.model.Instance.L1NpcInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.Instance.L1PetInstance;
import l1j.server.server.model.Instance.L1SummonInstance;
import l1j.server.server.model.map.L1Map;
import l1j.server.server.model.poison.L1Poison;
import l1j.server.server.model.skill.L1SkillTimer;
import l1j.server.server.model.skill.L1SkillTimerCreator;
import l1j.server.server.serverpackets.S_HPMeter;
import l1j.server.server.serverpackets.S_Light;
import l1j.server.server.serverpackets.S_PetCtrlMenu;
import l1j.server.server.serverpackets.S_Poison;
import l1j.server.server.serverpackets.S_RemoveObject;
import l1j.server.server.serverpackets.S_SystemMessage;
import l1j.server.server.serverpackets.ServerBasePacket;
import l1j.server.server.types.Point;
import l1j.server.server.utils.IntRange;

public class L1Character extends L1Object {

	private static final long serialVersionUID = 1L;
 
	private static Logger _log = Logger.getLogger(L1Character.class.getName());
	private L1Poison _poison = null;
	private boolean _paralyzed;
	private boolean _sleeped;

	private final HashMap<Integer, L1NpcInstance> _petlist = new HashMap<Integer, L1NpcInstance>();
//	private final HashMap<Integer, L1DollInstance> _dolllist = new HashMap<Integer, L1DollInstance>();
//	private final HashMap<Integer, L1DollInstance> _dolllistByItem = new HashMap<Integer, L1DollInstance>();
	private final HashMap<Integer, L1SkillTimer> _skillEffect = new HashMap<Integer, L1SkillTimer>();
	private final HashMap<Integer, L1ItemDelay.ItemDelayTimer> _itemdelay = new HashMap<Integer, L1ItemDelay.ItemDelayTimer>();
	private final HashMap<Integer, L1FollowerInstance> _followerlist = new HashMap<Integer, L1FollowerInstance>();
	// 正向
	protected static final byte HEADING_TABLE_X[] = { 0, 1, 1, 1, 0, -1, -1, -1 };
	protected static final byte HEADING_TABLE_Y[] = { -1, -1, 0, 1, 1, 1, 0, -1 };
	private int _currentHp;

	private int _currentMp;

	L1Paralysis _paralysis;

	private boolean _isSkillDelay = false;

	private long _exp; // ● 経験値

	// ■■■■■■■■■■ L1PcInstanceへ移動するプロパティ ■■■■■■■■■■
	private final List<L1Object> _knownObjects = new CopyOnWriteArrayList<L1Object>();

	private final List<L1PcInstance> _knownPlayer = new CopyOnWriteArrayList<L1PcInstance>();

	// ■■■■■■■■■■ プロパティ ■■■■■■■■■■
	//TODO 實裝 牛 豬 雞 鴨 對話 use srwh code 2/2
	public static final int STATE_IDLE = 0;

	public static final int STATE_RANDOM_WALK = 1;

	public static final int STATE_ATTACKING = 2;

	public static final int STATE_PICKUP_ITEM = 3;

	private int _currentState; // 不明

	private String _name; // ● 名前

	private int _level; // ● レベル

	private int _maxHp = 0; // ● ＭＡＸＨＰ（1～32767）

	private int _trueMaxHp = 0; // ● 本当のＭＡＸＨＰ

	private short _maxMp = 0; // ● ＭＡＸＭＰ（0～32767）

	private int _trueMaxMp = 0; // ● 本当のＭＡＸＭＰ

	private int _ac = 0; // ● ＡＣ（-128～127）

	private int _trueAc = 0; // ● 本当のＡＣ

	private short _str = 0; // ● ＳＴＲ（1～255）

	private short _trueStr = 0; // ● 本当のＳＴＲ

	private short _con = 0; // ● ＣＯＮ（1～255）

	private short _trueCon = 0; // ● 本当のＣＯＮ

	private short _dex = 0; // ● ＤＥＸ（1～255）

	private short _trueDex = 0; // ● 本当のＤＥＸ

	private short _cha = 0; // ● ＣＨＡ（1～255）

	private short _trueCha = 0; // ● 本当のＣＨＡ

	private short _int = 0; // ● ＩＮＴ（1～255）

	private short _trueInt = 0; // ● 本当のＩＮＴ

	private short _wis = 0; // ● ＷＩＳ（1～255）

	private short _trueWis = 0; // ● 本当のＷＩＳ

	private int _wind = 0; // ● 風防御（-128～127）

	private int _trueWind = 0; // ● 本当の風防御

	private int _water = 0; // ● 水防御（-128～127）

	private int _trueWater = 0; // ● 本当の水防御

	private int _fire = 0; // ● 火防御（-128～127）

	private int _trueFire = 0; // ● 本当の火防御

	private int _earth = 0; // ● 地防御（-128～127）

	private int _trueEarth = 0; // ● 本当の地防御

	private int _addAttrKind; // エレメンタルフォールダウンで減少した属性の種類

	// 昏迷耐性
	private int _registStun = 0;

	private int _trueRegistStun = 0;

	// 石化耐性
	private int _registStone = 0;

	private int _trueRegistStone = 0;

	// 睡眠耐性
	private int _registSleep = 0;

	private int _trueRegistSleep = 0;

	// 凍結耐性
	private int _registFreeze = 0;

	private int _trueRegistFreeze = 0;

	// 支撐耐性
	private int _registSustain = 0;

	private int _trueRegistSustain = 0;

	// 暗闇耐性
	private int _registBlind = 0;

	private int _trueRegistBlind = 0;

	private int _dmgup = 0; // ● ダメージ補正（-128～127）

	private int _trueDmgup = 0; // ● 本当のダメージ補正

	private int _bowDmgup = 0; // ● 弓ダメージ補正（-128～127）

	private int _trueBowDmgup = 0; // ● 本当の弓ダメージ補正

	private int _hitup = 0; // ● 命中補正（-128～127）

	private int _trueHitup = 0; // ● 本当の命中補正

	private int _bowHitup = 0; // ● 弓命中補正（-128～127）

	private int _trueBowHitup = 0; // ● 本当の弓命中補正
	private int _mr = 0; // ● 魔法防御（0～）

	private int _trueMr = 0; // ● 本当の魔法防御

	private int _sp = 0; // ● 増加したＳＰ

	private boolean _isDead; // ● 死亡状態

	private int _status; // ● 状態？

	private String _title; // ● タイトル

	private int _lawful; // ● アライメント

	private int _heading; // ● 向き 0.左上 1.上 2.右上 3.右 4.右下 5.下 6.左下 7.左
	private int _moveSpeed; // ● スピード 0.通常 1.ヘイスト 2.スロー
	private int _braveSpeed; // ● ブレイブ状態 0.通常 1.ブレイブ
	private int _tempCharGfx; // ● ベースグラフィックＩＤ

	private int _gfxid; // ● グラフィックＩＤ

	private int _karma;

	//TODO 記錄轉生次數
	private  int _metempsychosis;

	private int _chaLightSize; // ● ライトの範囲

	private int _ownLightSize; // ● ライトの範囲(S_OwnCharPack用)

	//TODO NPC說話順序判斷
	private int _order = 0;

	// 飽食度
	private int _food;

	/** 閃避率 +
	 * @author  by yajackk2001
	 */
	private byte _dodge = 0;

	/** 閃避率 -
	 * @author  by yajackk2001
	 */
	private byte _nDodge = 0;

	//opqlo 額外攻擊力
	private int _extradmg;
	//opqlo 額外防禦力
	private int _extradef;

	//opqlo 怪物減傷倍率
	private byte _dmgreducerate;

	/** false=小怪 true=大怪*/
	public boolean isBigSize;

	private double isBig =0;

	private double isSmall =0;
	public L1Character() {
		this._level = 1;
		this._currentState = 0;//TODO 實裝 牛 豬 雞 鴨 對話 use srwh code 1/2
	}

	public void addAc(final int i) {
		this.setAc(this._trueAc + i);
		if(this._ac <-211){
			if(this instanceof L1PcInstance){
				final L1PcInstance pc = (L1PcInstance)this;
				pc.sendPackets(new S_SystemMessage("防禦變更為:" +this._ac));
			}
		}
	}

	public void addBowDmgup(final int i) {
		this._trueBowDmgup += i;
		if (this._trueBowDmgup >= 127) {
			this._bowDmgup = 127;
		} else if (this._trueBowDmgup <= -128) {
			this._bowDmgup = -128;
		} else {
			this._bowDmgup = this._trueBowDmgup;
		}
	}

	public void addBowHitup(final int i) {
		this._trueBowHitup += i;
		if (this._trueBowHitup >= 127) {
			this._bowHitup = 127;
		} else if (this._trueBowHitup <= -128) {
			this._bowHitup = -128;
		} else {
			this._bowHitup = this._trueBowHitup;
		}
	}

	public void addCha(final int i) {
		this.setCha(this._trueCha + i);
	}
	public void addCon(final int i) {
		this.setCon(this._trueCon + i);
	}

	public void addDex(int i) {
		this.setDex(this._trueDex + i);
	}

	public void addDmgup(final int i) {
		this._trueDmgup += i;
		if (this._trueDmgup >= 127) {
			this._dmgup = 127;
		} else if (this._trueDmgup <= -128) {
			this._dmgup = -128;
		} else {
			this._dmgup = this._trueDmgup;
		}
	}

	public void addDodge(final byte i) {
		this._dodge += i;
		if (this._dodge >= 10) {
			this._dodge = 10;
		} else if (this._dodge <= 0){
			this._dodge = 0;
		}
	}
	/** 閃避率 +
	 * @author  by yajackk2001
	 */

	/**
	 * キャラクターへマジックドールを追加する。
	 *
	 * @param doll
	 *            追加するdollを表す、L1DollInstanceオブジェクト。
	 */
//	public void addDoll(final L1DollInstance doll) {
//		this._dolllist.put(doll.getId(), doll);
//		this._dolllistByItem.put(doll.getItemObjId(), doll);
//	}

	public void addEarth(final int i) {
		this._trueEarth += i;
		if (this._trueEarth >= 127) {
			this._earth = 127;
		} else if (this._trueEarth <= -128) {
			this._earth = -128;
		} else {
			this._earth = this._trueEarth;
		}
	}

	public void addExtraDef(final int i){
		this._extradef+=i;
	}

	public void addExtraDmg(final int i){
		this._extradmg+=i;
	}

	public void addFire(final int i) {
		this._trueFire += i;
		if (this._trueFire >= 127) {
			this._fire = 127;
		} else if (this._trueFire <= -128) {
			this._fire = -128;
		} else {
			this._fire = this._trueFire;
		}
	}

	/**
	 * キャラクターへ従者を追加する。
	 *
	 * @param follower
	 *            追加するfollowerを表す、L1FollowerInstanceオブジェクト。
	 */
	public void addFollower(final L1FollowerInstance follower) {
		this._followerlist.put(follower.getId(), follower);
	}

	public void addHitup(final int i) {
		this._trueHitup += i;
		if(this instanceof L1PcInstance){
			if (this._trueHitup >= 127) {
				this._hitup = 127;
			} else if (this._trueHitup <= -128) {
				this._hitup = -128;
			} else {
				this._hitup = this._trueHitup;
			}
		}else{
			this._hitup = this._trueHitup;
		}
	}

	public void addInt(final int i) {
		this.setInt(this._trueInt + i);
	}

	/**
	 * キャラクターへ、アイテムディレイを追加する。
	 *
	 * @param delayId
	 *            アイテムディレイID。 通常のアイテムであれば0、インビジビリティ クローク、バルログ ブラッディ クロークであれば1。
	 * @param timer
	 *            ディレイ時間を表す、L1ItemDelay.ItemDelayTimerオブジェクト。
	 */
	public void addItemDelay(final int delayId,final  L1ItemDelay.ItemDelayTimer timer) {
		this._itemdelay.put(delayId, timer);
	}

	/**
	 * 加入已認識物件
	 *
	 * @param obj 加入對象
	 */
	public void addKnownObject(final L1Object obj) {
		if (!this._knownObjects.contains(obj)) {
			this._knownObjects.add(obj);
			if (obj instanceof L1PcInstance) {
				this._knownPlayer.add((L1PcInstance) obj);
			}
		}
	}

	public void addMaxHp(final int i) {
		this.setMaxHp(this._trueMaxHp + i);

	}

	public void addMaxMp(final int i) {
		this.setMaxMp(this._trueMaxMp + i);
	}

	public void addMr(final int i) {
		this._trueMr += i;
		if (this._trueMr <= 0) {
			this._mr = 0;
		} else {
			this._mr = this._trueMr;
		}
	}

	/** 閃避率 -
	 * @author  by yajackk2001
	 */
	public void addNdodge(final byte i) {
		this._nDodge += i;
		if (this._nDodge >= 10) {
			this._nDodge = 10;
		} else if (this._nDodge <= 0){
			this._nDodge = 0;
		}

	}

	/**
	 * キャラクターへ、新たにペット、サモンモンスター、テイミングモンスター、あるいはクリエイトゾンビを追加する。
	 *
	 * @param npc
	 *            追加するNpcを表す、L1NpcInstanceオブジェクト。
	 */
	public void addPet(final L1NpcInstance npc) {
		this._petlist.put(npc.getId(), npc);
		//TODO 3.5C顯示寵物控制圖形介面 by99團隊
		//if (_petlist.size() < 2) {
		this.sendPetCtrlMenu(npc, true);
		//}
		//TODO 3.5C顯示寵物控制圖形介面 by99團隊
	}

	public void addRegistBlind(final int i) {
		this._trueRegistBlind += i;
		if (this._trueRegistBlind > 127) {
			this._registBlind = 127;
		} else if (this._trueRegistBlind < -128) {
			this._registBlind = -128;
		} else {
			this._registBlind = this._trueRegistBlind;
		}
	}

	public void addRegistFreeze(final int i) {
		this._trueRegistFreeze += i;
		if (this._trueRegistFreeze > 127) {
			this._registFreeze = 127;
		} else if (this._trueRegistFreeze < -128) {
			this._registFreeze = -128;
		} else {
			this._registFreeze = this._trueRegistFreeze;
		}
	}

	public void addRegistSleep(final int i) {
		this._trueRegistSleep += i;
		if (this._trueRegistSleep > 127) {
			this._registSleep = 127;
		} else if (this._trueRegistSleep < -128) {
			this._registSleep = -128;
		} else {
			this._registSleep = this._trueRegistSleep;
		}
	}

	public void addRegistStone(final int i) {
		this._trueRegistStone += i;
		if (this._trueRegistStone > 127) {
			this._registStone = 127;
		} else if (this._trueRegistStone < -128) {
			this._registStone = -128;
		} else {
			this._registStone = this._trueRegistStone;
		}
	}

	//昏迷耐性
	public void addRegistStun(final int i) {
		this._trueRegistStun += i;
		if (this._trueRegistStun > 127) {
			this._registStun = 127;
		} else if (this._trueRegistStun < -128) {
			this._registStun = -128;
		} else {
			this._registStun = this._trueRegistStun;
		}
	}

	//支撐耐性
	public void addRegistSustain(final int i) {
		this._trueRegistSustain += i;
		if (this._trueRegistSustain > 127) {
			this._registSustain = 127;
		} else if (this._trueRegistSustain < -128) {
			this._registSustain = -128;
		} else {
			this._registSustain = this._trueRegistSustain;
		}
	}

	/**
	 * キャラクターへ、新たにスキル効果を追加する。
	 *
	 * @param skillId
	 *            追加する効果のスキルID。
	 * @param timeMillis
	 *            追加する効果の持続時間。無限の場合は0。
	 */
	private void addSkillEffect(final int skillId,final  int timeMillis) {
		L1SkillTimer timer = null;
		if (0 < timeMillis) {
			timer = L1SkillTimerCreator.create(this, skillId, timeMillis);
			timer.begin();
		}
		this._skillEffect.put(skillId, timer);
	}

	public void addSp(final int i) {
		this._sp += i;
	}

	public void addStr(final int i) {
		this.setStr(this._trueStr + i);
	}

	public void addWater(final int i) {
		this._trueWater += i;
		if (this._trueWater >= 127) {
			this._water = 127;
		} else if (this._trueWater <= -128) {
			this._water = -128;
		} else {
			this._water = this._trueWater;
		}

	}

	public void addWind(final int i) {
		this._trueWind += i;
		if (this._trueWind >= 127) {
			this._wind = 127;
		} else if (this._trueWind <= -128) {
			this._wind = -128;
		} else {
			this._wind = this._trueWind;
		}

	}

	public void addWis(final int i) {
		this.setWis(this._trueWis + i);
	}

	/**
	 * キャラクターの可視範囲に居るプレイヤーへ、パケットを送信する。
	 *
	 * @param packet
	 *            送信するパケットを表すServerBasePacketオブジェクト。
	 */
	//	public void broadcastPacketAll(final ServerBasePacket packet) {
	//		for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this)) {
	//			/*	//opqlo 關閉特效
	//			String  s =packet.toString();
	//			if(s.startsWith("o")&&pc.getIsEffectOff()){
	//				continue;
	//
	//			}*/
	//			if(!pc.isTeleport()){
	//				pc.sendPackets(packet);
	//			}
	//			//opqlo 關閉特效
	//		}
	//	}

	/**
	 * 該物件全部可見範圍封包發送(不包含自己)
	 * @param packet 封包
	 */
	public void broadcastPacketAll(final ServerBasePacket packet) {
		try {
			for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this)) {
				if(!pc.isTeleport()){
					pc.sendPackets(packet);
				}
			}

		} catch (final Exception e) {
			_log.log(Level.WARNING,e.getLocalizedMessage(), e);
		}
	}

	/**
	 * キャラクターの可視範囲に居るプレイヤーへ、パケットを送信する。 ただしターゲットの画面内には送信しない。
	 *
	 * @param packet
	 *            送信するパケットを表すServerBasePacketオブジェクト。
	 */
	public void broadcastPacketExceptTargetSight(final ServerBasePacket packet,final L1Character target) {
		for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayerExceptTargetSight(this, target)) {
			if(!pc.isTeleport()){
				pc.sendPackets(packet);
			}
		}
	}
	/**
	 * キャラクターの可視範囲でインビジを見破れるor見破れないプレイヤーを区別して、パケットを送信する。
	 *
	 * @param packet
	 *            送信するパケットを表すServerBasePacketオブジェクト。
	 * @param isFindInvis
	 *            true : 見破れるプレイヤーにだけパケットを送信する。 false : 見破れないプレイヤーにだけパケットを送信する。
	 */
	public void broadcastPacketForFindInvis(final ServerBasePacket packet,final boolean isFindInvis) {
		for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this)) {
			if (isFindInvis) {
				if (pc.hasSkillEffect(GMSTATUS_FINDINVIS)) {
					pc.sendPackets(packet);
				}
			} else {
				if (!pc.hasSkillEffect(GMSTATUS_FINDINVIS)) {
					pc.sendPackets(packet);
				}
			}
		}
	}

	/**
	 * 該物件指定範圍封包發送(範圍10)
	 * @param packet 封包
	 */
	public void broadcastPacketX10(final ServerBasePacket packet) {
		try {
			for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this, 10)) {
				if(!pc.isTeleport()){
					pc.sendPackets(packet);
				}
			}

		} catch (final Exception e) {
			_log.log(Level.WARNING,e.getLocalizedMessage(), e);
		}
	}

	/**
	 * 該物件指定範圍封包發送(範圍8)
	 * @param packet 封包
	 */
	public void broadcastPacketX8(final ServerBasePacket packet) {
		try {
			for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this, 8)) {
				if(!pc.isTeleport()){
					pc.sendPackets(packet);
				}
			}

		} catch (final Exception e) {
			_log.log(Level.WARNING,e.getLocalizedMessage(), e);
		}
	}

	/**
	 * キャラクターから、全てのスキル効果タイマーを削除する。スキル効果は削除されない。
	 */
	public void clearSkillEffectTimer() {
		for (final L1SkillTimer timer : this._skillEffect.values()) {
			if (timer != null) {
				timer.kill();
			}
		}
		this._skillEffect.clear();
	}
	public void cureParalaysis() {
		if (this._paralysis != null) {
			this._paralysis.cure();
		}
	}

	/**
	 * キャラクターの毒を治療する。
	 */
	public void curePoison() {
		if (this._poison == null) {
			return;
		}
		this._poison.cure();
	}

	public int get_currentState() {
		return this._currentState;
	}
	public int get_food() {
		return this._food;
	}
	public int get_metempsychosis()
	{
		return this._metempsychosis;
	}

	//TODO 魔法娃娃DB化
	public int getAc() {
		return this._ac ; // TODO マジックドール效果 - AC増加
	}
	//TODO 魔法娃娃DB化

	public int getAddAttrKind() {
		return this._addAttrKind;
	}

	//TODO 魔法娃娃DB化
	public int getBowDmgup() {
		if(this instanceof L1PcInstance){
			return this._bowDmgup + ((L1PcInstance)this).getDollList().getBowDmg();
		}
		return this._bowDmgup ;
		// TODO　マジックドール效果　弓一般遠距離額外
	}
	//TODO 魔法娃娃DB化
	//TODO 魔法娃娃DB化
	public int getBowHitup() {
		if(this instanceof L1PcInstance){
			return this._bowHitup + ((L1PcInstance)this).getDollList().getBowHit();
		}
		return this._bowHitup ;
		// TODO　マジックドール效果　弓一般遠距離命中
	}
	//TODO 魔法娃娃DB化

	public int getBraveSpeed() {
		return this._braveSpeed;
	}

	public short getCha() {
		return this._cha;
	}

	public int getChaLightSize() {
		if (this.isInvisble()) {
			return 0;
		}
		return this._chaLightSize;
	}

	public short getCon() {
		return this._con;
	}

	/**
	 * キャラクターの現在のHPを返す。
	 *
	 * @return 現在のHP
	 */
	public int getCurrentHp() {
		return this._currentHp;
	}

	/**
	 * キャラクターの現在のMPを返す。
	 *
	 * @return 現在のMP
	 */
	public int getCurrentMp() {
		return this._currentMp;
	}
	public short getDex() {
		return this._dex;
	}

	public byte getDmgReduceRate(){
		return this._dmgreducerate;
	}
	public int getDmgup() {
		return this._dmgup;
	} // 使用するとき

	public byte getDodge() {
		return this._dodge;
	}
//	/**用道具流水號取得娃娃**/
//	public L1DollInstance getDollByItem(final int itemid) {
//		return this._dolllistByItem.get(itemid);
//	}

	/**
	 * キャラクターのマジックドールリストを返す。
	 *
	 * @return キャラクターの魔法人形リストを表す、HashMapオブジェクト。このオブジェクトのKeyはオブジェクトID、
	 *         ValueはL1DollInstance。
	 */
//	public HashMap<Integer, L1DollInstance> getDollList() {
//		return this._dolllist;
//	}


	public int getEarth() {
		return this._earth;
	} // 使用するとき

	/**
	 * キャラクターが保持している経験値を返す。
	 *
	 * @return 経験値。
	 */
	public long getExp() {
		return this._exp;
	}
	public int getExtraDef(){
		return this._extradef;
	}


	public int getExtraDmg(){
		return this._extradmg;
	}

	public int getFire() {
		return this._fire;
	} // 使用するとき

	/**
	 * キャラクターの従者リストを返す。
	 *
	 * @return キャラクターの従者リストを表す、HashMapオブジェクト。このオブジェクトのKeyはオブジェクトID、
	 *         ValueはL1FollowerInstance。
	 */
	public HashMap<Integer, L1FollowerInstance> getFollowerList() {
		return this._followerlist;
	}
	/**
	 * キャラクターの正面の座標を返す。
	 *
	 * @return 正面の座標
	 */
	public int[] getFrontLoc() {
		final int[] loc = new int[2];
		int x = this.getX();
		int y = this.getY();
		final int heading = this.getHeading();
		if (heading == 0) {
			y--;
		} else if (heading == 1) {
			x++;
			y--;
		} else if (heading == 2) {
			x++;
		} else if (heading == 3) {
			x++;
			y++;
		} else if (heading == 4) {
			y++;
		} else if (heading == 5) {
			x--;
			y++;
		} else if (heading == 6) {
			x--;
		} else if (heading == 7) {
			x--;
			y--;
		}
		loc[0] = x;
		loc[1] = y;
		return loc;
	}

	public int getGfxId() {
		return this._gfxid;
	}
	public int getHeading() {
		return this._heading;
	}

	//TODO 魔法娃娃DB化
	public int getHitup() {
		if(this instanceof L1PcInstance){
			return this._hitup + ((L1PcInstance)this).getDollList().getHit();
		}
		return this._hitup;

		// TODO マジックドール效果　近距離攻撃命中率増加
	}
	//TODO 魔法娃娃DB化
	public short getInt() {
		return this._int;
	}

	/**
	 * キャラクターのインベントリを返す。
	 *
	 * @return キャラクターのインベントリを表す、L1Inventoryオブジェクト。
	 */
	public L1Inventory getInventory() {
		return null;
	}


	/**
	 * キャラクターのアイテムディレイ時間を表す、L1ItemDelay.ItemDelayTimerを返す。
	 *
	 * @param delayId
	 *            調べるアイテムディレイID。 通常のアイテムであれば0、インビジビリティ クローク、バルログ ブラッディ
	 *            クロークであれば1。
	 * @return アイテムディレイ時間を表す、L1ItemDelay.ItemDelayTimer。
	 */
	public L1ItemDelay.ItemDelayTimer getItemDelayTimer(final int delayId) {
		return this._itemdelay.get(delayId);
	}

	/**
	 * キャラクターが保持しているカルマを返す。
	 *
	 * @return カルマ。
	 */
	public int getKarma() {
		return this._karma;
	}
	/**
	 * 全部認識物件(L1Object)清單
	 *
	 * @return 全部認識物件(L1Object)清單
	 */
	public List<L1Object> getKnownObjects() {
		return this._knownObjects;
	}

	/**
	 * 全部認識物件(PC)清單
	 *
	 * @return 全部認識物件(PC)清單
	 */
	public List<L1PcInstance> getKnownPlayers() {
		return this._knownPlayer;
	}

	/**
	 * 設定正義質
	 * @param i
	 */
	public void setLawful(final int i) {
		this._lawful = i;
	}
	/**
	 * 傳回正義質
	 * @return
	 */
	public int getLawful() {
		return this._lawful;
	}

	public synchronized int getLevel() {
		return this._level;
	}
	/**
	 * 智力命中魔法追加
	 * @return
	 */
	public int getMagicBonus() {
		switch (this.getInt()) {
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
			return -2;

		case 6:
		case 7:
		case 8:
			return -1;

		case 9:
		case 10:
		case 11:
			return 0;

		case 12:
		case 13:
		case 14:
			return 1;

		case 15:
		case 16:
		case 17:
			return 2;

		case 18:
			return 3;
		case 19:
			return 4;
		case 20:
			return 5;
		case 21:
			return 6;
		case 22:
			return 7;
		case 23:
			return 8;
		case 24:
			return 9;
		case 25:
		case 26:
		case 27:
			return 10;
		case 28:
		case 29:
		case 30:
			return 11;
		case 31:
		case 32:
		case 33:
			return 12;
		case 34:
		case 35:
		case 36:
			return 13;
		case 37:
		case 38:
		case 39:
			return 14;
		case 40:
		case 41:
		case 42:
		case 43:
			return 15;
			/*case 44:
			return 17;
		case 45:
			return 18;
		case 46:
			return 19;
		case 47:
			return 20;
		case 48:
			return 21;
		case 49:
			return 22;
		case 50:
			return 23;
		case 51:
			return 24;
		case 52:
			return 25;
		case 53:
			return 26;
		case 54:
			return 27;
		case 55:
			return 28;
		case 56:
			return 29;
		case 57:
			return 30;
		case 58:
			return 31;
		case 59:
			return 32;
		case 60:
			return 33;
		case 61:
			return 34;
		case 62:
			return 35;
		case 63:
			return 36;
		case 64:
			return 37;
		case 65:
			return 38;
		case 66:
			return 39;
		case 67:
			return 40;
		case 68:
			return 41;
		case 69:
			return 42;
		case 70:
			return 43;
		case 71:
			return 44;
		case 72:
			return 45;
		case 73:
			return 46;
		case 74:
			return 47;
		case 75:
			return 48;
		case 76:
			return 49;
		case 77:
			return 50;
		case 78:
			return 51;
		case 79:
			return 52;
		case 80:
			return 53;
		case 81:
			return 54;
		case 82:
			return 55;
		case 83:
			return 56;
		case 84:
			return 57;
		case 85:
			return 58;
		case 86:
			return 59;
		case 87:
			return 60;
		case 88:
			return 61;
		case 89:
			return 62;
		case 90:
			return 63;*/
		default:
			return this.getInt() - 28;
		}
	}

	/**
	 * 魔法等級
	 * @return
	 */
	public int getMagicLevel() {
		return this.getLevel() >> 2;// / 4;
	}

	public int getMaxHp() {
		return this._maxHp;
	}

	public short getMaxMp() {
		return this._maxMp;
	}
	public int getMoveSpeed() {
		return this._moveSpeed;
	}

	public int getMr() {
		if (this.hasSkillEffect(153) == true) {
			return this._mr / 4;
		} else {
			return this._mr;
		}
	} // 使用するとき

	public String getName() {
		return this._name;
	}

	public byte getNdodge() {
		return this._nDodge;
	}
	public int getOrder() {
		return this._order;
	}

	public int getOwnLightSize() {
		return this._ownLightSize;
	}

	public L1Paralysis getParalysis() {
		return this._paralysis;
	}

	/**
	 * キャラクターのペットリストを返す。
	 *
	 * @return
	 *         キャラクターのペットリストを表す、HashMapオブジェクト。このオブジェクトのKeyはオブジェクトID、ValueはL1NpcInstance
	 *         。
	 */
	public HashMap<Integer, L1NpcInstance> getPetList() {
		return this._petlist;
	}
	/**
	 * キャラクターの毒状態を返す。
	 *
	 * @return キャラクターの毒を表す、L1Poisonオブジェクト。
	 */
	public L1Poison getPoison() {
		return this._poison;
	}

	public int getRegistBlind() {
		if(this instanceof L1PcInstance){
			return this._registBlind +((L1PcInstance)this).getRegistBlind();
		}
		return this._registBlind ;
		// TODO マジックドール效果 　暗闇耐性
	}

	public int getRegistFreeze() {
		if(this instanceof L1PcInstance){
			return this._registFreeze +((L1PcInstance)this).getRegistFreeze();
		}
		return this._registFreeze;
		// TODO　マジックドール效果　凍結耐性
	}

	public int getRegistSleep() {
		if(this instanceof L1PcInstance){
			return this._registSleep +((L1PcInstance)this).getRegistSleep();
		}
		return this._registSleep;
		// 　TODO マジックドール効果　睡眠耐性増加
	}
	public int getRegistStone() {
		if(this instanceof L1PcInstance){
			return this._registStone +((L1PcInstance)this).getRegistStone();
		}
		return this._registStone;
		// 　TODO マジックドール効果　石化耐性増加
	}

	public int getRegistStun() {
		if(this instanceof L1PcInstance){
			return this._registStun +((L1PcInstance)this).getRegistSustain();
		}
		return this._registStun;
		// 　TODO　マジックドール効果　昏迷耐性増加
	}

	public int getRegistSustain() {
		if(this instanceof L1PcInstance){
			return this._registSustain +((L1PcInstance)this).getRegistSustain();
		}
		return this._registSustain;
		// TODO マジックドール效果 　支撐耐性
	}

	/**取得大小怪物增傷倍率*/
	public double getSizeDmg(final boolean isBigSize){

		return isBigSize==true? this.isBig:this.isSmall;
	}

	/**
	 * 該物件目前具有的技能編號
	 * @return
	 */
	public Set<Integer> getSkillEffect() {
		return this._skillEffect.keySet();
	}
	/**
	 * キャラクターのスキル効果の持続時間を返す。
	 *
	 * @param skillId
	 *            調べる効果のスキルID
	 * @return スキル効果の残り時間(秒)。スキルがかかっていないか効果時間が無限の場合、-1。
	 */
	public int getSkillEffectTimeSec(final int skillId) {
		final L1SkillTimer timer = this._skillEffect.get(skillId);
		if (timer == null) {
			return -1;
		}
		return timer.getRemainingTime();
	}

	/**
	 * 該物件目前具有技能狀態
	 * @return true:沒有 false:有
	 */
	public boolean getSkillisEmpty() {
		return this._skillEffect.isEmpty();
	}

	public int getSp() {
		int sp = this._sp;
		if(this.hasSkillEffect(ILLUSION_LICH)){
			sp +=2;
		}
		if(this.hasSkillEffect(ILLUSION_AVATAR)){
			sp += 10;
		}
		return this.getTrueSp() + sp;
	}

	public int getStatus() {
		return this._status;
	}

	public short getStr() {
		return this._str;
	}

	public int getTempCharGfx() {
		return this._tempCharGfx;
	}

	public String getTitle() {
		return this._title;
	}

	public int getTrueMr() {
		return this._trueMr;
	} // セットするとき

	public int getTrueSp() {
		return this.getMagicLevel() + this.getMagicBonus();
	}

	public int getWater() {
		return this._water;
	} // 使用するとき

	public int getWind() {
		return this._wind;
	} // 使用するとき

	public short getWis() {
		return this._wis;
	}

	/**
	 * キャラクターが存在する座標が、どのゾーンに属しているかを返す。
	 *
	 * @return 座標のゾーンを表す値。セーフティーゾーンであれば1、コンバットゾーンであれば-1、ノーマルゾーンであれば0。
	 */
	public int getZoneType() {
		if (this.getMap().isSafetyZone(this.getLocation())) {
			return 1;
		} else if (this.getMap().isCombatZone(this.getLocation())) {
			return -1;
		} else { // ノーマルゾーン
			return 0;
		}
	}

	/**
	 * 指定された座標までの直線上に、障害物が存在*しないか*を返す。
	 *
	 * @param tx
	 *            座標のX値
	 * @param ty
	 *            座標のY値
	 * @return 障害物が無ければtrue、あればfalseを返す。
	 */
	public boolean glanceCheck(final int tx, final int ty) {
		final L1Map map = getMap();
		int chx = getX();
		int chy = getY();
		//final int arw = 0;

		for (int i = 0; i < 15; i++) {
			if (((chx == tx) && (chy == ty)) || ((chx + 1 == tx) && (chy - 1 == ty))
					|| ((chx + 1 == tx) && (chy == ty))
					|| ((chx + 1 == tx) && (chy + 1 == ty))
					|| ((chx == tx) && (chy + 1 == ty))
					|| ((chx - 1 == tx) && (chy + 1 == ty))
					|| ((chx - 1 == tx) && (chy == ty))
					|| ((chx - 1 == tx) && (chy - 1 == ty))
					|| ((chx == tx) && (chy - 1 == ty))) {
				break;

			} else {
				int th = targetDirection(tx, ty);
				if (!map.isArrowPassable(chx, chy, th)) {
					return false;
				}
				if (chx < tx) {
					if (chy == ty) {
						chx++;

					} else if (chy > ty) {
						chx++;
						chy--;

					} else if (chy < ty) {
						chx++;
						chy++;

					}

				} else if (chx == tx) {
					if (chy < ty) {
						chy++;

					} else if (chy > ty) {
						chy--;
					}

				} else if (chx > tx) {
					if (chy == ty) {
						chx--;

					} else if (chy < ty) {
						chx--;
						chy++;

					} else if (chy > ty) {
						chx--;
						chy--;
					}
				}
			}
		}
		return true;
	}

	/**
	 * キャラクターに、アイテムディレイがあるかを返す。
	 *
	 * @param delayId
	 *            調べるアイテムディレイID。 通常のアイテムであれば0、インビジビリティ クローク、バルログ ブラッディ
	 *            クロークであれば1。
	 * @return アイテムディレイがあればtrue、なければfalse。
	 */
	public boolean hasItemDelay(final int delayId) {
		return this._itemdelay.containsKey(delayId);
	}

	/**
	 * キャラクターに、スキル効果が掛かっているかを返す。
	 *
	 * @param skillId
	 *            調べる効果のスキルID。
	 * @return 魔法効果があればtrue、なければfalse。
	 */
	public boolean hasSkillEffect(final int skillId) {
		return this._skillEffect.containsKey(skillId);
	}

	public void healHp(final int pt) {
		this.setCurrentHp(this.getCurrentHp() + pt);
	}

	/**
	 * 指定された座標へ攻撃可能であるかを返す。
	 *
	 * @param x
	 *            座標のX値。
	 * @param y
	 *            座標のY値。
	 * @param range
	 *            攻撃可能な範囲(タイル数)
	 * @return 攻撃可能であればtrue,不可能であればfalse
	 */
	public boolean isAttackPosition(final int x,final  int y,final  int range) {
		if (range >= 7) // 遠隔武器（７以上の場合斜めを考慮すると画面外に出る)
		{
			if (this.getLocation().getTileDistance(new Point(x, y)) > range) {
				return false;
			}
		} else // 近接武器
		{
			if (this.getLocation().getTileLineDistance(new Point(x, y)) > range) {
				return false;
			}
		}
		return this.glanceCheck(x, y);
	}

	public boolean isDead() {
		return this._isDead;
	}

	public boolean isInvisble() {
		return this.hasSkillEffect(INVISIBILITY) || this.hasSkillEffect(BLIND_HIDING);
	}

	/**
	 * キャラクターの麻痺状態を返す。
	 *
	 * @return 麻痺状態を表す値。麻痺状態であればtrue。
	 */
	public boolean isParalyzed() {
		return this._paralyzed;
	}

	/**
	 * キャラクターの毒状態を返す。
	 *
	 * @return スキルディレイ中か。
	 */
	public boolean isSkillDelay() {
		return this._isSkillDelay;
	}

	/**
	 * キャラクターの眠り状態を返す。
	 *
	 * @return 眠り状態を表す値。眠り状態であればtrue。
	 */
	public boolean isSleeped() {
		return this._sleeped;
	}

	/**
	 * キャラクターから、スキル効果のタイマーを削除する。 スキル効果は削除されない。
	 *
	 * @param skillId
	 *            削除するタイマーのスキルＩＤ
	 */
	public void killSkillEffectTimer(final int skillId) {
		final L1SkillTimer timer = this._skillEffect.remove(skillId);
		if (timer != null) {
			timer.kill();
		}
	}

	/**
	 * 是否為已認識物件
	 *
	 * @param obj 判斷對象
	 * @return true:是 false:不是
	 */
	public boolean knownsObject(final L1Object obj) {
		return this._knownObjects.contains(obj);
	}

	/**
	 * キャラクターから、全ての認識しているオブジェクトを削除する。
	 */
	public void removeAllKnownObjects() {
		this._knownObjects.clear();
		this._knownPlayer.clear();
	}

	/**
	 * キャラクターからマジックドールを削除する。
	 *
	 * @param doll
	 *            削除するdollを表す、L1DollInstanceオブジェクト。
	 */
//	public void removeDoll(final L1DollInstance doll) {
//		this._dolllist.remove(doll.getId());
//		this._dolllistByItem.remove(doll.getItemObjId());
//	}

	/**
	 * キャラクターから従者を削除する。
	 *
	 * @param follower
	 *            削除するfollowerを表す、L1FollowerInstanceオブジェクト。
	 */
	public void removeFollower(final L1FollowerInstance follower) {
		this._followerlist.remove(follower.getId());
	}

	/**
	 * キャラクターから、アイテムディレイを削除する。
	 *
	 * @param delayId
	 *            アイテムディレイID。 通常のアイテムであれば0、インビジビリティ クローク、バルログ ブラッディ クロークであれば1。
	 */
	public void removeItemDelay(final int delayId) {
		this._itemdelay.remove(delayId);
	}

	/**
	 * 移出已認識物件
	 *
	 * @param obj 移出對象
	 */
	public void removeKnownObject(final L1Object obj) {
		this._knownObjects.remove(obj);
		if (obj instanceof L1PcInstance) {
			this._knownPlayer.remove(obj);
		}
	}

	/**
	 * キャラクターから、ペット、サモンモンスター、テイミングモンスター、あるいはクリエイトゾンビを削除する。
	 *
	 * @param npc
	 *            削除するNpcを表す、L1NpcInstanceオブジェクト。
	 */
	public void removePet(final L1NpcInstance npc) {
		this._petlist.remove(npc.getId());
		//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
		//if (_petlist.isEmpty()) {
		this.sendPetCtrlMenu(npc, false);
		//}
		//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
	}

	/**
	 * キャラクターから、スキル効果を削除する。
	 *
	 * @param skillId
	 *            削除する効果のスキルID
	 */
	public void removeSkillEffect(final int skillId) {
		final L1SkillTimer timer = this._skillEffect.remove(skillId);
		if (timer != null) {
			timer.end();
		}
	}

	/**
	 * キャラクターを復活させる。
	 *
	 * @param hp
	 *            復活後のHP
	 */
	public void resurrect(int hp) {
		if (!this.isDead()) {
			return;
		}
		if (hp <= 0) {
			hp = 1;
		}
		this.setCurrentHp(hp);
		this.setDead(false);
		this.setStatus(0);
		this.clearSkillEffectTimer();//TODO 角色死亡復活技能消除by狼人香
		L1PolyMorph.undoPoly(this);
		for (final L1PcInstance pc : L1World.getInstance().getRecognizePlayer(this)) {
			pc.sendPackets(new S_RemoveObject(this));
			pc.removeKnownObject(this);
			pc.updateObject();
		}
	}

	/**
	 * 3.3C PetMenu 控制
	 * @param npc
	 * @param type
	 * 1:顯示 0:關閉
	 */
	public void sendPetCtrlMenu(final L1NpcInstance npc,final  boolean type) {
		if (npc instanceof L1PetInstance) {
			final L1PetInstance pet = (L1PetInstance) npc;
			final L1Character cha = pet.getMaster();
			if (cha instanceof L1PcInstance) {
				final L1PcInstance pc = (L1PcInstance) cha;
				//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
				pc.sendPackets(new S_PetCtrlMenu(cha, npc, type));
				//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
			}
		} else if (npc instanceof L1SummonInstance) {
			final L1SummonInstance summon = (L1SummonInstance) npc;
			final L1Character cha = summon.getMaster();
			if (cha instanceof L1PcInstance) {
				final L1PcInstance pc = (L1PcInstance) cha;
				//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
				pc.sendPackets(new S_PetCtrlMenu(cha, npc, type));
				//TODO 3.5C關閉寵物圖型介面UI變更by99團隊
			}
		}
	}

	public void set_currentState(final int i) {
		this._currentState = i;
	}
	//TODO 實裝 牛 豬 雞 鴨 對話 use srwh code 2/2

	public void set_food(final int i) {
		this._food = i;
	}

	public void set_metempsychosis(final int l)
	{
		this._metempsychosis = l;
	}
	//TODO 記錄轉生次數

	//opqlo 七屬性破127
	public void setAc(final int i) {
		this._trueAc = i;
		this._ac = IntRange.ensure(this._trueAc, -255, 255);
	}

	public void setAddAttrKind(final int i) {
		this._addAttrKind = i;
	}

	public void setBigDmg(final double d){
		this.isBig = d;
	}
	public void setBraveSpeed(final int i) {
		this._braveSpeed = i;
	}

	public void setCha(final int i) {
		this._trueCha = (short) i;
		this._cha = this._trueCha;
		//		_cha = (short) IntRange.ensure(i, 1, 255);
	}


	public void setChaLightSize(final int i) {
		this._chaLightSize = i;
	}

	public void setCon(final int i) {
		this._trueCon = (short) i;
		this._con = this._trueCon;
		//	_con = (short) IntRange.ensure(i, 1, 255);
	}

	/**
	 * キャラクターのHPを設定する。
	 *
	 * @param i
	 *            キャラクターの新しいHP
	 */
	// 特殊な処理がある場合はこっちをオーバライド（パケット送信等）
	public void setCurrentHp(final int i) {
		this._currentHp = i;
		if (this._currentHp >= this.getMaxHp()) {
			this._currentHp = this.getMaxHp();
		}
	}

	/**
	 * キャラクターのHPを設定する。
	 *
	 * @param i
	 *            キャラクターの新しいHP
	 */
	public void setCurrentHpDirect(final int i) {
		this._currentHp = i;
	}

	/**
	 * キャラクターのMPを設定する。
	 *
	 * @param i
	 *            キャラクターの新しいMP
	 */
	// 特殊な処理がある場合はこっちをオーバライド（パケット送信等）
	public void setCurrentMp(final int i) {
		this._currentMp = i;
		if (this._currentMp >= this.getMaxMp()) {
			this._currentMp = this.getMaxMp();
		}
	}

	/**
	 * キャラクターのMPを設定する。
	 *
	 * @param i
	 *            キャラクターの新しいMP
	 */
	public void setCurrentMpDirect(final int i) {
		this._currentMp = i;
	}

	public void setDead(final boolean flag) {
		this._isDead = flag;
	}
	public void setDex(final int i) {
		this._trueDex = (short) i;
		this._dex = this._trueDex;
		//		_dex = (short) IntRange.ensure(i, 1, 255);
	}

	public void setDmgReduceRate(final byte i){
		this._dmgreducerate = i;
	}

	/**
	 * キャラクターが保持する経験値を設定する。
	 *
	 * @param l
	 *            経験値。
	 */
	public void setExp(final long l) {
		this._exp = l;
	}


	public void setExtraDef(final int i){
		this._extradef=i;
	}

	public void setExtraDmg(final int i){
		this._extradmg=i;
	}

	public void setGfxId(final int i) {
		this._gfxid = i;
	}


	public void setHeading(final int i) {
		this._heading = i;
	}
	public void setInt(final int i) {
		this._trueInt = (short) i;
		this._int = this._trueInt;
		//		_int = (short) IntRange.ensure(i, 1, 255);
	}

	/**
	 * キャラクターが保持するカルマを設定する。
	 *
	 * @param karma
	 *            カルマ。
	 */
	public void setKarma(final int karma) {
		this._karma = karma;
	}
	public synchronized void setLevel(final long level) {
		this._level = (int) level;
	}

	public void setMaxHp(final int hp) {
		this._trueMaxHp = hp;
		this._maxHp = IntRange.ensure(this._trueMaxHp, 1 , 1000000000);
		this._currentHp = Math.min(this._currentHp, this._maxHp);

	}
	public void setMaxMp(final int mp) {
		this._trueMaxMp = mp;
		this._maxMp = (short) IntRange.ensure(this._trueMaxMp, 0, 32767);
		this._currentMp = Math.min(this._currentMp, this._maxMp);
	}

	public void setMoveSpeed(final int i) {
		this._moveSpeed = i;
	}
	public void setMr(final int i) {
		this._trueMr = i;
		if (this._trueMr <= 0) {
			this._mr = 0;
		} else {
			this._mr = this._trueMr;
		}

	}
	public void setName(String s) {
		this._name = s;
	}
	public void setOrder(final int i) {
		this._order = i;
	}
	//TODO NPC說話順序判斷

	public void setOwnLightSize(final int i) {
		this._ownLightSize = i;
	}
	public void setParalaysis(final L1Paralysis p) {
		this._paralysis = p;
	}
	/**
	 * キャラクターの麻痺状態を設定する。
	 *
	 * @param i
	 *            麻痺状態を表す値。麻痺状態であればtrue。
	 */
	public void setParalyzed(final boolean paralyzed) {
		this._paralyzed = paralyzed;
	}
	/**
	 * キャラクターへ、毒を追加する。
	 *
	 * @param poison
	 *            毒を表す、L1Poisonオブジェクト。
	 */
	public void setPoison(final L1Poison poison) {
		this._poison = poison;
	}

	/**
	 * キャラクターへ毒のエフェクトを付加する
	 *
	 * @param effectId
	 * @see S_Poison#S_Poison(int, int)
	 */
	public void setPoisonEffect(final int effectId) {
		this.broadcastPacketAll(new S_Poison(this.getId(), effectId));
	}
	/**
	 * キャラクターへ、スキルディレイを追加する。
	 *
	 * @param flag
	 */
	public void setSkillDelay(final boolean flag) {
		this._isSkillDelay = flag;
	}
	/**
	 * キャラクターへ、スキル効果を設定する。<br>
	 * 重複するスキルがない場合は、新たにスキル効果を追加する。<br>
	 * 重複するスキルがある場合は、残り効果時間とパラメータの効果時間の長い方を優先して設定する。
	 *
	 * @param skillId
	 *            設定する効果のスキルID。
	 * @param timeMillis
	 *            設定する効果の持続時間。無限の場合は0。
	 */
	public void setSkillEffect(final int skillId,final  int timeMillis) {
		if (this.hasSkillEffect(skillId)) {
			final int remainingTimeMills = this.getSkillEffectTimeSec(skillId) * 1000;

			// 残り時間が有限で、パラメータの効果時間の方が長いか無限の場合は上書きする。
			if (remainingTimeMills >= 0
					&& (remainingTimeMills < timeMillis || timeMillis == 0)) {
				this.killSkillEffectTimer(skillId);
				this.addSkillEffect(skillId, timeMillis);
			}
		} else {
			this.addSkillEffect(skillId, timeMillis);
		}
	}


	/**
	 * キャラクターの眠り状態を設定する。
	 *
	 * @param sleeped
	 *            眠り状態を表す値。眠り状態であればtrue。
	 */
	public void setSleeped(final boolean sleeped) {
		this._sleeped = sleeped;
	}

	public void setSmallDmg(final double d){
		this.isSmall = d;
	}
	public void setStatus(final int i) {
		this._status = i;
	}
	public void setStr(final int i) {
		this._trueStr = (short) i;
		this._str = this._trueStr;
		//_str = (short) IntRange.ensure(i, 1, 255);
	}
	public void setTempCharGfx(final int i) {
		this._tempCharGfx = i;
	}

	public void setTitle(String s) {
		this._title = s;
	}
	public void setWis(final int i) {
		this._trueWis = (short) i;
		this._wis = this._trueWis;
		//		_wis = (short) IntRange.ensure(i, 1, 255);
	}
	/**
	 * 指定された座標に対する方向を返す。
	 *
	 * @param tx
	 *            座標のX値
	 * @param ty
	 *            座標のY値
	 * @return 指定された座標に対する方向
	 */
	public int targetDirection(final int tx,final int ty) {
		final float dis_x = Math.abs(this.getX() - tx); // Ｘ方向のターゲットまでの距離
		final float dis_y = Math.abs(this.getY() - ty); // Ｙ方向のターゲットまでの距離
		final float dis = Math.max(dis_x, dis_y); // ターゲットまでの距離
		if (dis == 0) {
			return this.getHeading(); // 同じ位置ならいま向いてる方向を返しとく
		}
		final int avg_x = (int) Math.floor(dis_x / dis + 0.59f); // 上下左右がちょっと優先な丸め
		final int avg_y = (int) Math.floor(dis_y / dis + 0.59f); // 上下左右がちょっと優先な丸め

		int dir_x = 0;
		int dir_y = 0;
		if (this.getX() < tx) {
			dir_x = 1;
		}else if (this.getX() > tx) {
			dir_x = -1;
		}
		if (this.getY() < ty) {
			dir_y = 1;
		}else if (this.getY() > ty) {
			dir_y = -1;
		}

		if (avg_x == 0) {
			dir_x = 0;
		}
		if (avg_y == 0) {
			dir_y = 0;
		}

		if (dir_x == 1 && dir_y == -1) {
			return 1; // 上
		}
		if (dir_x == 1 && dir_y == 0) {
			return 2; // 右上
		}
		if (dir_x == 1 && dir_y == 1) {
			return 3; // 右
		}
		if (dir_x == 0 && dir_y == 1) {
			return 4; // 右下
		}
		if (dir_x == -1 && dir_y == 1) {
			return 5; // 下
		}
		if (dir_x == -1 && dir_y == 0) {
			return 6; // 左下
		}
		if (dir_x == -1 && dir_y == -1) {
			return 7; // 左
		}
		if (dir_x == 0 && dir_y == -1) {
			return 0; // 左上
		}
		return this.getHeading(); // ここにはこない。はず
	}
	public void turnOnOffLight() {
		int lightSize = 0;
		if (this instanceof L1NpcInstance) {
			final L1NpcInstance npc = (L1NpcInstance) this;
			lightSize = npc.getLightSize(); // npc.sqlのライトサイズ
		}
		if (this.hasSkillEffect(LIGHT)) {
			lightSize = 14;
		}

		for (final L1ItemInstance item : this.getInventory().getItems()) {
			if (item.getItem().getType2() == 0 && item.getItem().getType() == 2) { // light系アイテム
				final int itemlightSize = item.getItem().getLightRange();
				if (itemlightSize != 0 && item.isNowLighting()) {
					if (itemlightSize > lightSize) {
						lightSize = itemlightSize;
					}
				}
			}
		}

		if (this instanceof L1PcInstance) {
			final L1PcInstance pc = (L1PcInstance) this;
			pc.sendPackets(new S_Light(pc.getId(), lightSize));
		}
		if (!this.isInvisble()) {
			this.broadcastPacketAll(new S_Light(this.getId(), lightSize));
		}

		this.setOwnLightSize(lightSize); // S_OwnCharPackのライト範囲
		this.setChaLightSize(lightSize); // S_OtherCharPack, S_NPCPackなどのライト範囲
	}
	/**
	 * キャラクターの50マス以内に居るプレイヤーへ、パケットを送信する。
	 *
	 * @param packet
	 *            送信するパケットを表すServerBasePacketオブジェクト。
	 */
	public void wideBroadcastPacket(final ServerBasePacket packet) {
		for (final L1PcInstance pc : L1World.getInstance().getVisiblePlayer(this, 50)) {
			pc.sendPackets(packet);
		}
	}
	private int _oldhp;//opqlo 血條封包變更 血量紀錄
	/**血量有變更才傳送封包**/
	public void HpBar(){
		try{
			if(this.getCurrentHp() != _oldhp){
				_oldhp = this.getCurrentHp();
				this.broadcastPacketAll(new S_HPMeter(this));
			}
		}catch(Exception e){
			_log.log(Level.SEVERE, e.getLocalizedMessage() , e );
		}
	}
}
